package p2p;

import java.io.File;
import java.io.FilePermission;
import java.io.FilenameFilter;
import java.net.SocketPermission;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import server.Message;

public class FileList {
	private Peer thisPeer;
	private NeighborList neighborList;
	private File folder;
	private List<File> flist = new ArrayList<File>();
	private Map<Peer, List<File>> peerToFlist = new HashMap<Peer, List<File>>();
	private int preNList = 0;
	private Map<String, List<Peer>> filenameToPeers = new HashMap<String, List<Peer>>();

	public FileList(Peer thisPeer, NeighborList neighborList, File folder) {
		this.thisPeer = thisPeer;
		this.neighborList = neighborList;
		this.folder = folder;
	}

	//PRECONDITION: The folders may only contain files
	public void update() {		
		Registry registry;
		Message rmiServer;

		boolean hasChange = false;

		/*
		 * Add / Delete files to peer
		 */
		File[] listOfFiles = folder.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String name) {
				if(name.startsWith(".")) return false;
				return true;
			}
		});
		for(int i=0; i<listOfFiles.length; i++) { //Any new files which are not in flist
			if (listOfFiles[i].isFile()) { //is file?
				File current = listOfFiles[i];
				if(!flist.contains(current)) { //add to flist if it is not part of the flist already
					hasChange = true; //change has occurred
					flist.add(current);
					peerToFlist.put(thisPeer, flist);
				}
			}
		}
		List<File> folderFiles = Arrays.asList(listOfFiles);
		if(!flist.isEmpty()) { //if it is not the first time the file system is checked
			Iterator<File> itr = flist.iterator();
		    while (itr.hasNext()) { //Any files which are in flist but not in the folder
		    	File f = itr.next();
		    	if(!folderFiles.contains(f)) { //Any files which are in the the filelist but not in the folder?
		    		hasChange = true; //change has occurred
		    		itr.remove();

				}
			}
		}

		List<Peer> nlist = neighborList.getNlist(); //nlist
		/*
		 * manage new neighbor files
		 */
		if(!hasChange) { //has change already happened?
			if(nlist.size() != preNList) {
				hasChange=true;
			}
		}
		
		/*
		 * manage neighbors flist
		 */
		if(hasChange) { //has change occurred?
			for(Peer p : nlist) {
				try {
					//send flist
					registry = LocateRegistry.getRegistry(p.getIp(), p.getPort()); //get the “registry” 
					rmiServer = (Message)(registry.lookup(Config.ServerName)); //look up remote object
					boolean success = rmiServer.reportFileList(thisPeer, Collections.unmodifiableList(flist));//call remote method
				} catch (NotBoundException e) {
					e.printStackTrace();
				} catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		}

		preNList = nlist.size();

		/*
		 * revert to Map<File, List<Peer> 
		 */
		updateFileToPeersMap();
	}

	public boolean reportFileList(Peer fromPeer, List<File> flist) {
		if(!fromPeer.equals(thisPeer)) { //not this peer
			peerToFlist.put(fromPeer, flist);
			updateFileToPeersMap();
			return true;
		}
		return false;
	}
	
	public void updateFileToPeersMap(){
		filenameToPeers.clear();
		Map<Peer, List<File>> copyPeerToFlist = Collections.unmodifiableMap(peerToFlist);
		for(Peer peer : copyPeerToFlist.keySet()){
			List<File> fileList = copyPeerToFlist.get(peer);
			for(File file: fileList){
				List<Peer> peerList = new ArrayList<Peer>();
				if(filenameToPeers.get(file.getName()) == null){
					peerList.add(peer);
					filenameToPeers.put(file.getName(), peerList);
				}else{
					peerList.addAll(filenameToPeers.get(file.getName()));
					peerList.add(peer);
					filenameToPeers.put(file.getName(), peerList);
				}
			}
		}
	}

	public List<Peer> findLocalFile(String fileName){
		List<Peer> listPeer = new ArrayList<Peer>();
		for(String myFile : filenameToPeers.keySet()){
			if(myFile.equals(fileName)){
				listPeer.addAll(filenameToPeers.get(myFile));
				break;
			}
		}
		return listPeer;
	}
	
	public String getFilePath(String fileName){
		for(File file : flist){
			if(file.getName().equals(fileName)) return file.getAbsolutePath();
		}
		return null;
	}
	
	public String getDirectory(){
		return folder.getAbsolutePath();
	}

	@Override
	public String toString() {
		String fList = "";

		for(String myFile : filenameToPeers.keySet()){
			String pList = "";
			List<Peer> peerList = filenameToPeers.get(myFile);
			for(Peer peer : peerList){
				if(pList.isEmpty()){
					pList = peer.getName();
				}else{
					pList = pList+", "+peer.getName();
				}
			}
			fList = fList + myFile +" : "+ pList+"\n";
		}
		return (fList.isEmpty()) ? "" : fList.substring(0, fList.length()-1);
	}
}
